昨天學習完了常用的 if 判斷式跟迴圈的寫法以及用法,今天要進入函式的定義與使用方法。隨著程式越寫越大,常常會有某些程式碼需要被重複使用。這時候就需要把程式拆分成一個個「函式」(在 C# 裡稱為方法 Method)。使用函式能讓程式碼更乾淨、易讀、也更好維護。
Method,就是一段程式碼區塊,裡面包含一系列要執行的敘述。當程式呼叫 (call) 這個方法時,方法內的程式碼才會執行,並且可以傳入必要的參數 (arguments)。在 C# 中,每一個指令都是在某個方法內被執行的。
一個Method的宣告包含以下幾個部分:
來看一段範例程式碼:
namespace MotorCycleExample
{
abstract class Motorcycle
{
// 任何人都能呼叫
public void StartEngine() { /* 方法內容 */ }
// 只有衍生類別 (繼承者) 可以呼叫
protected void AddGas(int gallons) { /* 方法內容 */ }
// 衍生類別可以覆寫 (override) 這個方法
public virtual int Drive(int miles, int speed)
{
/* 方法內容 */
return 1;
}
// 方法多載 (overloading):同名方法但參數不同
public virtual int Drive(TimeSpan time, int speed)
{
/* 方法內容 */
return 0;
}
// 抽象方法:強制衍生類別必須實作
public abstract double GetTopSpeed();
}
}
在這段程式碼內的一些重點整理如下:
在 C# 裡,方法 (method) 有兩種形式:
在呼叫時會根據 Instance method 以及 Static method 有不同的呼叫方式,如果是 實例方法 → 使用 物件名稱.方法名稱(參數)。如果是 靜態方法 → 使用 類別名稱.方法名稱(參數)。括號裡的內容就是 引數 (arguments),多個引數用逗號分隔。而其中 Method 裡面又分兩種,參數 (Parameters) 與引數 (Arguments)。參數 (parameter) → 在方法定義裡指定的名稱與型別。引數 (argument) → 呼叫方法時傳入的實際值。引數必須與參數型別相容,但引數名稱不用跟參數名稱相同。
public static class SquareExample
{
public static void Main()
{
// Call with an int variable.
int num = 4;
int productA = Square(num);
// Call with an integer literal.
int productB = Square(12);
// Call with an expression that evaluates to int.
int productC = Square(productA * 3);
}
static int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
}
這個類別 SquareExample 定義了一個靜態 Square 函式,Main 在程式啟動時呼叫 Square 三次,分別傳入變數、常數與運算式,計算平方並把結果存到三個 product 變數中。
最常見的呼叫方式是 位置引數,就是按照參數的順序一一對應。
namespace MotorCycleExample
{
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
// Only derived classes can call this.
public void AddGas(int gallons) { /* Method statements here */ }
// Derived classes can override the base class implementation.
public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }
// Derived classes can override the base class implementation.
public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
class TestMotorcycle : Motorcycle
{
public override double GetTopSpeed() => 108.4;
static void Main()
{
var moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
_ = moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine($"My top speed is {speed}");
}
}
}
繼承了前面的 Motorcycle ,調用了前面的面的參數,執行後的結果會得到->My top speed is 108.4
也可以用 具名引數,直接指定參數名稱:
namespace MotorCycleExample
{
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
// Only derived classes can call this.
public void AddGas(int gallons) { /* Method statements here */ }
// Derived classes can override the base class implementation.
public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }
// Derived classes can override the base class implementation.
public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
class TestMotorcycle : Motorcycle
{
public override int Drive(int miles, int speed) =>
(int)Math.Round((double)miles / speed, 0);
public override double GetTopSpeed() => 108.4;
static void Main()
{
var moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
int travelTime = moto.Drive(speed: 60, miles: 170);
Console.WriteLine($"Travel time: approx. {travelTime} hours");
}
}
}
得到的結果為->Travel time: approx. 3 hours
與上一個範例不同的在於其優點,順序可以不同,只要有把參數名稱寫清楚。
今天所學習的內容對我來說稍微有些困難,感覺還要再花點時間消化,而且也沒達到預期的進度,所以明天會繼續把這個部分給學習完畢!今天的參考資料